package com.gack.netty.handler;

import java.util.Date;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.gack.netty.Repostory.NettyIpBlacklistRepository;
import com.gack.netty.constant.ConstantChannels;
import com.gack.netty.dao.NettyIpBlacklistDao;
import com.gack.netty.dispatcher.ServerDispatcher;
import com.gack.netty.po.NettyIpBlacklist;
import com.gack.netty.proto.MessageModule.Message;
import com.gack.netty.util.common.ExceptionUtil;
import com.gack.netty.util.common.IpUtil;
import com.gack.netty.vo.ChannelInfoVO;

import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.extern.slf4j.Slf4j;

/**
 * 
* @ClassName: NettyServerHandler 
* @Description: TODO(这里用一句话描述这个类的作用) 
* @author (ZhangXinYu)  
* @date 2018年8月30日 下午12:45:31 
* @version V1.0
 */
@Component
@Sharable
@Slf4j
public class NettyServerHandler extends ChannelInboundHandlerAdapter{

	@Autowired
	private NettyIpBlacklistDao ipBlackListDao;
	@Autowired
	private NettyIpBlacklistRepository ipBlackListRepository;	
	
	@Value("${netty.channelBusy.busyTime}")
	private int BUSY_TIME;
	@Value("${netty.channelBusy.busyCount}")
	private int BUSY_COUNT;
	@Value("${netty.message_time_out}")
	private int MESSAGE_TIME_OUT;
	
	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		log.info("-------------------------NETTY INFO-------------------------");
		log.info("客户端["+ctx.channel().remoteAddress()+"] 上线");
		super.channelActive(ctx);
	}

	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		log.info("-------------------------NETTY INFO-------------------------");
		log.info("客户端["+ctx.channel().remoteAddress()+"] 掉线");
		super.channelInactive(ctx);
	}

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		Message message = (Message)msg;
		
		// if message timeStamp is too long ago , refuse process data
		if (Math.abs(System.currentTimeMillis() - message.getTimestamp()) > MESSAGE_TIME_OUT) {
			log.info("-------------------------NETTY INFO-------------------------");
			log.info("客户端["+ctx.channel().remoteAddress()+"] 发送信息已超时失效,服务器已主动过滤该消息");
			log.info("消息内容: {msgType:" + message.getMsgType() + ", content:" + message.getContent()
				+ ", timestamp:"+message.getTimestamp() + ", equipment: " + message.getEquipment() + "}");
			return;
		}
		
		// if client ip in blacklist , close channel
		Set<String> ipDisableSet = ipBlackListDao.allDisableIp();
		if(ipDisableSet != null && ipDisableSet.size() > 0){
			if(ipDisableSet.contains(IpUtil.getIp(ctx.channel().remoteAddress().toString()))){
				// the client ip is disabled , close channel and not send message
				ctx.channel().close();
				log.info("-------------------------NETTY INFO-------------------------");
				log.info("客户端["+ctx.channel().remoteAddress().toString()+"] ip被禁用  连接已关闭");
				return;
			}
		}
		
		// if client send too much message, the client will be shield and close channel
		ChannelInfoVO channelInfo = ConstantChannels.get(ctx.channel());
		Date nowTime = new Date();
		// reset channel is busy to receive message
		if(Math.abs(System.currentTimeMillis() - channelInfo.getReceiveMessageEndTime().getTime()) >= BUSY_TIME){
			// if channel not busy, set receiveTime then do nothing
			channelInfo.setBusy(false);
			channelInfo.setReceiveMessageBeginTime(nowTime);
			channelInfo.setReceiveMessageEndTime(nowTime);
			channelInfo.setReceiveMessageCount(0);
		} else {
			// if channel is busy , then set channel receiveMessagEndTime
			channelInfo.setBusy(true);
			channelInfo.setReceiveMessageEndTime(nowTime);
			channelInfo.setReceiveMessageCount(channelInfo.getReceiveMessageCount() + 1);	// receiveMessageCount ++
			// if channel busyCount >= 200
			if(channelInfo.getReceiveMessageCount() >= BUSY_COUNT){
				NettyIpBlacklist nettyIpBlacklist = new NettyIpBlacklist();
				nettyIpBlacklist.setIp(IpUtil.getIp(ctx.channel().remoteAddress().toString()));
				nettyIpBlacklist.setCreate_time(nowTime);
				nettyIpBlacklist = ipBlackListRepository.save(nettyIpBlacklist);
				ctx.channel().close();
				log.info("-------------------------NETTY INFO-------------------------");
				log.info("客户端["+ctx.channel().remoteAddress().toString()+"] 请求过于频繁，已被关闭连接并且将ip加入黑名单中，禁止ip访问");
				// then do nothing and end method
				return;
			}
		}
		// add channelInfo for channel
		ConstantChannels.push(ctx.channel(), channelInfo);
		
		// format and print message
		log.info("-------------------------NETTY INFO-------------------------");
		log.info("已接收到客户端["+ctx.channel().remoteAddress()+"] 发送过来的消息");
		log.info("消息内容: {msgType:" + message.getMsgType() + ", content:" + message.getContent()
			+ ", timestamp:"+message.getTimestamp() + ", equipment: " + message.getEquipment() + "}");
		// the message will by send to different service to be processing , according to message.msgType
		ServerDispatcher.submit(ctx.channel(), msg);
		super.channelRead(ctx, msg);
	}

	@Override
	public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
		ctx.channel().flush();
		log.info("-------------------------NETTY INFO-------------------------");
		log.info("客户端["+ctx.channel().remoteAddress()+"] 发送的消息已读取并处理完成");
		super.channelReadComplete(ctx);
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		if(!ctx.channel().isActive()){
			// client close channel exception (IOException)
			System.out.println("客户端["+ctx.channel().remoteAddress()+"] 主动断开连接");
			super.exceptionCaught(ctx, cause);
			return;
		}
		
		log.error("-------------------------NETTY ERROR-------------------------");
		log.error("客户端["+ctx.channel().remoteAddress()+"] 出现异常,将打印异常并关闭客户端连接");
		log.error("异常信息>>" + cause.toString());
		log.error("堆栈信息>>");
		String[] stackTraceArray = ExceptionUtil.getThrowableStrRep(cause);
		for(String stackTrace : stackTraceArray){
			log.error(stackTrace);
		}
		
		cause.printStackTrace();
		
		// remove channel from channelMap
		ConstantChannels.remove(ctx.channel());
		// close channel
		ctx.close();
	}

	@Override
	public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
		// if client ip in blacklist , close channel
		Set<String> ipDisableSet = ipBlackListDao.allDisableIp();
		if(ipDisableSet != null && ipDisableSet.size() > 0){
			if(ipDisableSet.contains(IpUtil.getIp(ctx.channel().remoteAddress().toString()))){
				// the client ip is disabled , close channel and not send message
				ctx.channel().close();
				log.info("-------------------------NETTY INFO-------------------------");
				log.info("客户端["+ctx.channel().remoteAddress().toString()+"] ip被禁用  连接已关闭");
				return;
			}
		}
		
		log.info("-------------------------NETTY INFO-------------------------");
		log.info("客户端["+ctx.channel().remoteAddress()+"] 已加入,将会将该客户端连接管道缓存起来  以备推送使用");
		
		// build channelInfo, then add channel and channelInfo to channelMap
		ChannelInfoVO channelInfo = new ChannelInfoVO();
		channelInfo.setAddress(ctx.channel().remoteAddress().toString());
		channelInfo.setAssets(null);
		channelInfo.setChannel(ctx.channel());
		channelInfo.setCreateTime(new Date());
		channelInfo.setEquipment(null);
		Date nowTime = new Date();
		channelInfo.setBusy(false);
		channelInfo.setReceiveMessageBeginTime(nowTime);
		channelInfo.setReceiveMessageEndTime(nowTime);
		channelInfo.setReceiveMessageCount(0);
		ConstantChannels.push(ctx.channel(),channelInfo);
		super.handlerAdded(ctx);
	}

	@Override
	public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
		log.info("-------------------------NETTY INFO-------------------------");
		log.info("客户端["+ctx.channel().remoteAddress()+"] 已离开,将会将该客户端连接管道移除缓存");
		
		// remove channel from channelMap
		ConstantChannels.remove(ctx.channel());
		super.handlerRemoved(ctx);
	}
	
}
